package com.unitech.order.service;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import com.unitech.common.enums.BusinessErrorCode;
import com.unitech.common.exception.BusinessException;
import com.unitech.drink.api.beans.DrinkInfo;
import com.unitech.drink.service.intf.DrinkService;
import com.unitech.order.api.beans.*;
import com.unitech.order.api.enums.OrderStatusType;
import com.unitech.order.constant.RabbitmqConstant;
import com.unitech.order.converter.OrderConverter;
import com.unitech.order.converter.OrderItemConverter;
import com.unitech.order.dao.OrderDrinkDao;
import com.unitech.order.dao.OrderDao;
import com.unitech.order.dao.entity.Order;
import com.unitech.order.dao.entity.OrderDrink;
import com.unitech.order.service.intf.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    private DrinkService drinkService;
    @Autowired
    private OrderDao orderDao;
    @Autowired
    private OrderDrinkDao orderDrinkDao;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /*
    生成订单id
     */
    @Override
    public String generateOrderId() {
        //简单使用
        Snowflake snowflake = IdUtil.getSnowflake(1, 1);
        return snowflake.nextIdStr();
    }

    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public OrderInfo createOrder(OrderParam request) {
        // 1. 插入数据到t_order表
        Order order = new Order();
        BeanUtils.copyProperties(request, order);
        // 1.1 计算总价
        // 1.1.1 取出所有饮品id
        List<Long> ids = request.getItems().stream()
                .map(item -> item.getId())
                .collect(Collectors.toList());
        // 1.1.2 通过id查询出所有drink
        Map<Long, BigDecimal> drinkPriceMap = drinkService.loadDrinks(ids).stream()
                .collect(Collectors.toMap(DrinkInfo::getId, DrinkInfo::getPrice));
        // 1.1.3 计算总价
        BigDecimal total = BigDecimal.ZERO;
        for(ItemParam item: request.getItems()) {
            BigDecimal itemPrice = drinkPriceMap.get(item.getId());

            // 使用order id和前端传入的drink id插入数据到中间表
            OrderDrink od = new OrderDrink();
            od.setOrderId(request.getId());
            od.setItemsId(item.getId());
            od.setItemsQuantity(item.getQuantity());
            od.setItemsPrice(itemPrice);

            // item总价 = 单价*数量
            BigDecimal itemTotal = itemPrice.multiply(BigDecimal.valueOf(item.getQuantity()));
            od.setItemsTotal(itemTotal);
            orderDrinkDao.insert(od);

            // order总价 = item1总价+...+item_n总价
            total = total.add(itemTotal);
        }
        order.setTotal(total);
        order.setCreateTime(LocalDateTime.now());
        order.setState(OrderStatusType.INIT.getCode());
        order.setVersion(0);
        // 插入t_order
        orderDao.insert(order);

        return OrderConverter.convertToOrderInfo(order);
    }

    @Override
    public OrderInfo loadOrder(String id) {
        // 查询订单头信息
        Order order = orderDao.selectByPrimaryKey(id);
        return OrderConverter.convertToOrderInfo(order);
    }

    @Override
    public List<ItemInfo> loadOrderItems(String id) {
        // 查询订单items信息
        Example example = new Example(OrderDrink.class);
        example.createCriteria().andEqualTo("orderId", id);
        List<OrderDrink> orderItems = orderDrinkDao.selectByExample(example);

        List<Long> ids = orderItems.stream()
                .map(drink -> drink.getItemsId())
                .collect(Collectors.toList());
        Map<Long, String> drinkNameMap = drinkService.loadDrinks(ids).stream()
                .collect(Collectors.toMap(DrinkInfo::getId, DrinkInfo::getName));

        List<ItemInfo> itemInfos = orderItems.stream()
                .map(drink -> {
                    ItemInfo itemInfo = OrderItemConverter.convertToItemInfo(drink);
                    itemInfo.setName(drinkNameMap.get(itemInfo.getId()));
                    return itemInfo;
                }).collect(Collectors.toList());

        return itemInfos;
    }

    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public OrderInfo updateOrderState(OrderChangeParam request) {
        log.info("更新订单信息: {}", request);
        // 1. 查询order头信息
        Order order = orderDao.selectByPrimaryKey(request.getId());
        // 2. 比较http请求中的版本和数据库中是否一致
        if (!order.getVersion().equals(request.getVersion())) {
            return null;
        } else {
            // 3. 相同则可以更新订单信息
            if (request.getState() <= order.getState()) {
                throw new BusinessException(BusinessErrorCode.FAILURE);
            }
            order.setState(request.getState());
            // 4. 版本更新+1
            order.setVersion(order.getVersion()+1);

            orderDao.updateByPrimaryKey(order);

            if (request.getState()==OrderStatusType.TEST.getCode()) {
                // 如果客户支付，那么通过消息队列发出支付信息，拉起第三方支付
                rabbitTemplate.convertAndSend(
                        RabbitmqConstant.EXCHANGE,
                        RabbitmqConstant.ROUTING_KEY,
                        request.getId());
            }

            return OrderConverter.convertToOrderInfo(order);
        }
    }

    @RabbitListener(queues = RabbitmqConstant.QUEUE)
    @RabbitHandler
    @Override
    public void pay(String orderId) {
        /*
        实际生产环境中需要开发单独的支付模块，并提供支付接口，系统自己的支付接口实现中会去拉起第三方支付接口(例如微信支付等).
        1. 下订单后向支付接口发送消息
        2. 支付成功不会有返回
         */
        log.info("收到支付请求，order ID: {}", orderId);
        long s = System.currentTimeMillis();
        try {
            Thread.sleep(30000); // 模拟长达1分钟的支付过程
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        log.info("支付完成，用时：{} ms", System.currentTimeMillis() - s);

        // 支付完成更新订单状态
        Order order = orderDao.selectByPrimaryKey(orderId);
        // 调用订单模块的订单更新接口
        order.setState(OrderStatusType.PAID.getCode());
        order.setVersion(order.getVersion()+1);
        orderDao.updateByPrimaryKey(order);
    }


}
